package me.timlong.service.impl;

import me.timlong.dao.PromoDOMapper;
import me.timlong.dataobject.PromoDO;
import me.timlong.error.BusinessException;
import me.timlong.error.EmBusinessError;
import me.timlong.service.ItemService;
import me.timlong.service.PromoService;
import me.timlong.service.UserService;
import me.timlong.service.model.ItemModel;
import me.timlong.service.model.PromoModel;
import me.timlong.service.model.UserModel;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class PromoServiceImpl implements PromoService {

    @Autowired
    private PromoDOMapper promoDOMapper;

    @Autowired
    private ItemService itemService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserService userService;

    @Override
    public PromoModel getPromoByItemId(Integer itemId) {

        PromoDO promoDO = promoDOMapper.selectByItemId(itemId);

        if (null == promoDO)
            return null;

        PromoModel promoModel = this.convertFromObject(promoDO);

        // 判断当前该商品是否有正在进行的和秒杀活动吗？
        if (promoModel.getStartTime().isAfterNow()) {
            promoModel.setStatus(1);
        } else if (promoModel.getEndTime().isBeforeNow()) {
            promoModel.setStatus(3);
        } else {
            promoModel.setStatus(2);
        }
        return promoModel;
    }

    @Override
    public void publishPromo(int promoId) {

        PromoDO promoDO = promoDOMapper.selectByPrimaryKey(promoId);
        if(promoDO.getItemId() == null || promoDO.getItemId() == 0) {
            return;
        }
        ItemModel itemModel = itemService.getItemById(promoDO.getItemId());
        // 库存同步到redis缓存
        redisTemplate.opsForValue().set("promo_item_stock_" + itemModel.getId(), itemModel.getStock());

        // 设置秒杀令牌发放的流量为当前商品库存的指定倍数，eg：5
        redisTemplate.opsForValue().set("promo_door_count_" + promoId, itemModel.getStock() * 5);
    }

    @Override
    public String generateSecondKillToken(Integer promoId, Integer userId, Integer itemId) {

        // 判断是否库存已经 售罄，若对应的售罄key存在，则直接返回下单失败
        if(redisTemplate.hasKey("promo_item_stock_invalid_" + itemId)) {
            return null;
        }

        PromoDO promoDO = promoDOMapper.selectByPrimaryKey(promoId);
        if(null == promoDO) {
            return null;
        }
        PromoModel promoModel = this.convertFromObject(promoDO);
        // 判断当前该商品是否有正在进行的和秒杀活动吗？
        if (promoModel.getStartTime().isAfterNow()) {
            promoModel.setStatus(1);
        } else if (promoModel.getEndTime().isBeforeNow()) {
            promoModel.setStatus(3);
        } else {
            promoModel.setStatus(2);
        }
        // 验证活动是否正在进行
        if(promoModel.getStatus().intValue() != 2) {
            return null;
        }

        // 验证用户信息
        ItemModel itemModel = itemService.getItemByIdInCache(itemId);
        if (null == itemModel) {
            return null;
        }

        // 验证商品信息
        UserModel userModel = userService.getUserByIdInCache(userId);
        if (null == userModel) {
            return null;
        }

        // 生成秒杀令牌， 并给一个5min的有效期
        String token = UUID.randomUUID().toString().replace("-", "");
        // 为了保证每个用户每个商品的秒杀令牌保持唯一性吗，这里key的拼接
        redisTemplate.opsForValue().set("promo_token_" + promoId + "_userid_" + userId + "_itemid_" + promoId, token);
        redisTemplate.expire("promo_token_" + promoId, 5, TimeUnit.MINUTES);

        return token;
    }

    private PromoModel convertFromObject(PromoDO promoDO) {
        if (null == promoDO)
            return null;
        PromoModel promoModel = new PromoModel();
        BeanUtils.copyProperties(promoDO, promoModel);
        promoModel.setPromoItemPrice(new BigDecimal(promoDO.getPromoItemPrice()));
        promoModel.setStartTime(new DateTime(promoDO.getStartDate()));
        promoModel.setEndTime(new DateTime(promoDO.getEndDate()));
        return promoModel;

    }
}
